/*****************************************************************************
 *   Copyright 2005 Tim A Wang
 *
 *   Licensed under the Apache License, Version 2.0 (the "License");
 *   you may not use this file except in compliance with the License.
 *   You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *   Unless required by applicable law or agreed to in writing, software
 *   distributed under the License is distributed on an "AS IS" BASIS,
 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *   See the License for the specific language governing permissions and
 *   limitations under the License.
 *
 ****************************************************************************/
package com.jb.web.content;

import com.jb.web.context.WebContext;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.Writer;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;


/**
 * A Content manager manages content for the entire application.
 * Content are files that can be loaded into the system and displayed
 * to a component through ${content.name} where name is a unique id for
 * a file that holds the content.
 *
 * The top directory where content is stored is fixed for this version, That is
 * webtop/resource.
 *
 * Contents are loaded based on language with in the web context.
 */
public class ContentManager extends HashMap {
    static final long serialVersionUID = 123123L;
    static private HashMap localeContentManager = new HashMap();
    protected String mLang;
    protected String mWebTop;
    private Log log = LogFactory.getLog(getClass().getName());
    private WebContext mCtx;
    private boolean personalizable;

    public ContentManager(WebContext pCtx) {
        mLang = pCtx.getSession().getLang();
        mWebTop = pCtx.getWebTop();
        mCtx = pCtx;
    }

    /**
     * Create a new instance of the content manager. It is likely one content
     * manager for each supported language.
     *
     * @param pCtx
     * @return
     */
    public static ContentManager getInstance(WebContext pCtx, boolean personalizable) {
        Object obj = localeContentManager.get(pCtx.getSession().getLang());

        if (obj == null) {
            obj = new ContentManager(pCtx);
        }

        ((ContentManager) obj).personalizable = personalizable;
        ((ContentManager) obj).mCtx = pCtx;

        return (ContentManager) obj;
    }

    public static ContentManager getInstance(WebContext pCtx) {
        return getInstance(pCtx, false);
    }

    public List getKeys() {
        return new ArrayList(this.keySet());
    }

    public String getLocation() {
        if (!this.personalizable) {
            return mCtx.getApplicationTop() + "/contents/" + mLang + "/";
        } else {
            return mCtx.getSession().getOwnerContentTop() + "/contents/" + mLang + "/";
        }
    }

    public synchronized void delete(Object key) {
        String ctxKey = makeKey(key.toString());

        if (log.isDebugEnabled()) {
            log.debug("delete conent " + key + ".txt");
        }

        File file = new File(getLocation() + key + ".txt");

        if (!file.exists()) {
            return;
        }

        file.delete();
        this.remove(ctxKey);
    }

    public Object get(Object key) {
        String ctxKey = makeKey(key.toString());
        Object value = (String) super.get(ctxKey);

        if (value == null) {
            value = loadContent(key);

            if (value != null) {
                put(ctxKey, value);
            }
        }

        return value;
    }

    public synchronized void saveAsContent(Object key, String content)
        throws Exception {
        try {
            if (log.isDebugEnabled()) {
                log.debug("save conent " + content + " to " + key + ".txt");
            }

            File file = new File(getLocation() + key + ".txt");

            if (file.exists()) {
                if (log.isErrorEnabled()) {
                    log.error("Content file already exists");
                }

                throw new ContentFileAlreadyExist("File Exists");
            }

            BufferedWriter writer = new BufferedWriter(new FileWriter(file));
            writer.write(content);

            String ctxKey = makeKey(key.toString());
            put(ctxKey, content);
            writer.close();
        } catch (IOException e) {
            if (log.isErrorEnabled()) {
                log.error("Failed to write resources.properties", e);
            }
        }
    }

    /**
     * Saves content into a directory, directory is located by pCtx getWebTop
     *
     * @param pCtx
     * @param key
     * @param content
     */
    public synchronized void saveContent(Object key, String content) {
        try {
            if (content == null) {
                return;
            }

            if (log.isDebugEnabled()) {
                log.debug("save conent " + content + " to " + key + ".txt");
            }

            File file = new File(getLocation());

            if (!file.exists()) {
                file.mkdirs();
            }

            FileOutputStream fos = new FileOutputStream(getLocation() + key + ".txt");
            Writer out = new OutputStreamWriter(fos, "UTF8");
            BufferedWriter writer = new BufferedWriter(out);
            writer.write(content);

            String ctxKey = makeKey(key.toString());
            put(ctxKey, content);
            writer.close();
            fos.close();
        } catch (IOException e) {
            if (log.isErrorEnabled()) {
                log.error("Failed to write resources.properties", e);
            }
        }
    }

    synchronized private String loadContent(Object key) {
        if (log.isDebugEnabled()) {
            log.debug("Load content " + key);
        }

        try {
            File file = new File(getLocation() + key + ".txt");

            if (!file.exists()) {
                return "";
            }

            FileInputStream fis = new FileInputStream(getLocation() + key + ".txt");
            InputStreamReader isr = new InputStreamReader(fis, "UTF8");

            BufferedReader reader = new BufferedReader(isr);

            StringBuffer value = new StringBuffer("");

            while (true) {
                String line = reader.readLine();

                if (line == null) {
                    break;
                }

                value.append(line);
            }

            reader.close();
            fis.close();

            return value.toString();
        } catch (Exception e) {
            if (log.isErrorEnabled()) {
                log.error("Failed to load resources.properties", e);
            }
        }

        return null;
    }

    private String makeKey(String key) {
        if (!this.personalizable) {
            return mLang + key;
        } else {
            return mLang + key + mCtx.getSession().getUser().getId();
        }
    }
}


class ContentFileAlreadyExist extends Exception {
    static final long serialVersionUID = 123123L;

    public ContentFileAlreadyExist(String errorMessage) {
        super(errorMessage);
    }
}
